home *** CD-ROM | disk | FTP | other *** search
- # IO::Socket.pm
- #
- # Copyright (c) 1996 Graham Barr <Graham.Barr@tiuk.ti.com>. All rights
- # reserved. This program is free software; you can redistribute it and/or
- # modify it under the same terms as Perl itself.
-
- package IO::Socket;
-
- =head1 NAME
-
- IO::Socket - Object interface to socket communications
-
- =head1 SYNOPSIS
-
- use IO::Socket;
-
- =head1 DESCRIPTION
-
- C<IO::Socket> provides an object interface to creating and using sockets. It
- is built upon the L<IO::Handle> interface and inherits all the methods defined
- by L<IO::Handle>.
-
- C<IO::Socket> only defines methods for those operations which are common to all
- types of socket. Operations which are specified to a socket in a particular
- domain have methods defined in sub classes of C<IO::Socket>
-
- C<IO::Socket> will export all functions (and constants) defined by L<Socket>.
-
- =head1 CONSTRUCTOR
-
- =over 4
-
- =item new ( [ARGS] )
-
- Creates an C<IO::Socket>, which is a reference to a
- newly created symbol (see the C<Symbol> package). C<new>
- optionally takes arguments, these arguments are in key-value pairs.
- C<new> only looks for one key C<Domain> which tells new which domain
- the socket will be in. All other arguments will be passed to the
- configuration method of the package for that domain, See below.
-
- C<IO::Socket>s will be in autoflush mode after creation. Note that
- versions of IO::Socket prior to 1.1603 (as shipped with Perl 5.004_04)
- did not do this. So if you need backward compatibility, you should
- set autoflush explicitly.
-
- =back
-
- =head1 METHODS
-
- See L<perlfunc> for complete descriptions of each of the following
- supported C<IO::Socket> methods, which are just front ends for the
- corresponding built-in functions:
-
- socket
- socketpair
- bind
- listen
- accept
- send
- recv
- peername (getpeername)
- sockname (getsockname)
-
- Some methods take slightly different arguments to those defined in L<perlfunc>
- in attempt to make the interface more flexible. These are
-
- =over 4
-
- =item accept([PKG])
-
- perform the system call C<accept> on the socket and return a new object. The
- new object will be created in the same class as the listen socket, unless
- C<PKG> is specified. This object can be used to communicate with the client
- that was trying to connect. In a scalar context the new socket is returned,
- or undef upon failure. In an array context a two-element array is returned
- containing the new socket and the peer address, the list will
- be empty upon failure.
-
- Additional methods that are provided are
-
- =item timeout([VAL])
-
- Set or get the timeout value associated with this socket. If called without
- any arguments then the current setting is returned. If called with an argument
- the current setting is changed and the previous value returned.
-
- =item sockopt(OPT [, VAL])
-
- Unified method to both set and get options in the SOL_SOCKET level. If called
- with one argument then getsockopt is called, otherwise setsockopt is called.
-
- =item sockdomain
-
- Returns the numerical number for the socket domain type. For example, for
- a AF_INET socket the value of &AF_INET will be returned.
-
- =item socktype
-
- Returns the numerical number for the socket type. For example, for
- a SOCK_STREAM socket the value of &SOCK_STREAM will be returned.
-
- =item protocol
-
- Returns the numerical number for the protocol being used on the socket, if
- known. If the protocol is unknown, as with an AF_UNIX socket, zero
- is returned.
-
- =back
-
- =cut
-
-
- require 5.000;
-
- use Config;
- use IO::Handle;
- use Socket 1.3;
- use Carp;
- use strict;
- use vars qw(@ISA $VERSION);
- use Exporter;
-
- @ISA = qw(IO::Handle);
-
- $VERSION = "1.1603";
-
- sub import {
- my $pkg = shift;
- my $callpkg = caller;
- Exporter::export 'Socket', $callpkg, @_;
- }
-
- sub new {
- my($class,%arg) = @_;
- my $fh = $class->SUPER::new();
- $fh->autoflush;
-
- ${*$fh}{'io_socket_timeout'} = delete $arg{Timeout};
-
- return scalar(%arg) ? $fh->configure(\%arg)
- : $fh;
- }
-
- my @domain2pkg = ();
-
- sub register_domain {
- my($p,$d) = @_;
- $domain2pkg[$d] = $p;
- }
-
- sub configure {
- my($fh,$arg) = @_;
- my $domain = delete $arg->{Domain};
-
- croak 'IO::Socket: Cannot configure a generic socket'
- unless defined $domain;
-
- croak "IO::Socket: Unsupported socket domain"
- unless defined $domain2pkg[$domain];
-
- croak "IO::Socket: Cannot configure socket in domain '$domain'"
- unless ref($fh) eq "IO::Socket";
-
- bless($fh, $domain2pkg[$domain]);
- $fh->configure($arg);
- }
-
- sub socket {
- @_ == 4 or croak 'usage: $fh->socket(DOMAIN, TYPE, PROTOCOL)';
- my($fh,$domain,$type,$protocol) = @_;
-
- socket($fh,$domain,$type,$protocol) or
- return undef;
-
- ${*$fh}{'io_socket_domain'} = $domain;
- ${*$fh}{'io_socket_type'} = $type;
- ${*$fh}{'io_socket_proto'} = $protocol;
-
- $fh;
- }
-
- sub socketpair {
- @_ == 4 || croak 'usage: IO::Socket->pair(DOMAIN, TYPE, PROTOCOL)';
- my($class,$domain,$type,$protocol) = @_;
- my $fh1 = $class->new();
- my $fh2 = $class->new();
-
- socketpair($fh1,$fh2,$domain,$type,$protocol) or
- return ();
-
- ${*$fh1}{'io_socket_type'} = ${*$fh2}{'io_socket_type'} = $type;
- ${*$fh1}{'io_socket_proto'} = ${*$fh2}{'io_socket_proto'} = $protocol;
-
- ($fh1,$fh2);
- }
-
- sub connect {
- @_ == 2 || @_ == 3 or croak 'usage: $fh->connect(NAME) or $fh->connect(PORT, ADDR)';
- my $fh = shift;
- my $addr = @_ == 1 ? shift : sockaddr_in(@_);
- my $timeout = ${*$fh}{'io_socket_timeout'};
- local($SIG{ALRM}) = $timeout ? sub { undef $fh; }
- : $SIG{ALRM} || 'DEFAULT';
-
- eval {
- croak 'connect: Bad address'
- if(@_ == 2 && !defined $_[1]);
-
- if($timeout) {
- defined $Config{d_alarm} && defined alarm($timeout) or
- $timeout = 0;
- }
-
- my $ok = connect($fh, $addr);
-
- alarm(0)
- if($timeout);
-
- croak "connect: timeout"
- unless defined $fh;
-
- undef $fh unless $ok;
- };
-
- $fh;
- }
-
- sub bind {
- @_ == 2 || @_ == 3 or croak 'usage: $fh->bind(NAME) or $fh->bind(PORT, ADDR)';
- my $fh = shift;
- my $addr = @_ == 1 ? shift : sockaddr_in(@_);
-
- return bind($fh, $addr) ? $fh
- : undef;
- }
-
- sub listen {
- @_ >= 1 && @_ <= 2 or croak 'usage: $fh->listen([QUEUE])';
- my($fh,$queue) = @_;
- $queue = 5
- unless $queue && $queue > 0;
-
- return listen($fh, $queue) ? $fh
- : undef;
- }
-
- sub accept {
- @_ == 1 || @_ == 2 or croak 'usage $fh->accept([PKG])';
- my $fh = shift;
- my $pkg = shift || $fh;
- my $timeout = ${*$fh}{'io_socket_timeout'};
- my $new = $pkg->new(Timeout => $timeout);
- my $peer = undef;
-
- eval {
- if($timeout) {
- my $fdset = "";
- vec($fdset, $fh->fileno,1) = 1;
- croak "accept: timeout"
- unless select($fdset,undef,undef,$timeout);
- }
- $peer = accept($new,$fh);
- };
-
- return wantarray ? defined $peer ? ($new, $peer)
- : ()
- : defined $peer ? $new
- : undef;
- }
-
- sub sockname {
- @_ == 1 or croak 'usage: $fh->sockname()';
- getsockname($_[0]);
- }
-
- sub peername {
- @_ == 1 or croak 'usage: $fh->peername()';
- my($fh) = @_;
- getpeername($fh)
- || ${*$fh}{'io_socket_peername'}
- || undef;
- }
-
- sub send {
- @_ >= 2 && @_ <= 4 or croak 'usage: $fh->send(BUF, [FLAGS, [TO]])';
- my $fh = $_[0];
- my $flags = $_[2] || 0;
- my $peer = $_[3] || $fh->peername;
-
- croak 'send: Cannot determine peer address'
- unless($peer);
-
- my $r = defined(getpeername($fh))
- ? send($fh, $_[1], $flags)
- : send($fh, $_[1], $flags, $peer);
-
- # remember who we send to, if it was sucessful
- ${*$fh}{'io_socket_peername'} = $peer
- if(@_ == 4 && defined $r);
-
- $r;
- }
-
- sub recv {
- @_ == 3 || @_ == 4 or croak 'usage: $fh->recv(BUF, LEN [, FLAGS])';
- my $sock = $_[0];
- my $len = $_[2];
- my $flags = $_[3] || 0;
-
- # remember who we recv'd from
- ${*$sock}{'io_socket_peername'} = recv($sock, $_[1]='', $len, $flags);
- }
-
-
- sub setsockopt {
- @_ == 4 or croak '$fh->setsockopt(LEVEL, OPTNAME)';
- setsockopt($_[0],$_[1],$_[2],$_[3]);
- }
-
- my $intsize = length(pack("i",0));
-
- sub getsockopt {
- @_ == 3 or croak '$fh->getsockopt(LEVEL, OPTNAME)';
- my $r = getsockopt($_[0],$_[1],$_[2]);
- # Just a guess
- $r = unpack("i", $r)
- if(defined $r && length($r) == $intsize);
- $r;
- }
-
- sub sockopt {
- my $fh = shift;
- @_ == 1 ? $fh->getsockopt(SOL_SOCKET,@_)
- : $fh->setsockopt(SOL_SOCKET,@_);
- }
-
- sub timeout {
- @_ == 1 || @_ == 2 or croak 'usage: $fh->timeout([VALUE])';
- my($fh,$val) = @_;
- my $r = ${*$fh}{'io_socket_timeout'} || undef;
-
- ${*$fh}{'io_socket_timeout'} = 0 + $val
- if(@_ == 2);
-
- $r;
- }
-
- sub sockdomain {
- @_ == 1 or croak 'usage: $fh->sockdomain()';
- my $fh = shift;
- ${*$fh}{'io_socket_domain'};
- }
-
- sub socktype {
- @_ == 1 or croak 'usage: $fh->socktype()';
- my $fh = shift;
- ${*$fh}{'io_socket_type'}
- }
-
- sub protocol {
- @_ == 1 or croak 'usage: $fh->protocol()';
- my($fh) = @_;
- ${*$fh}{'io_socket_protocol'};
- }
-
- =head1 SUB-CLASSES
-
- =cut
-
- ##
- ## AF_INET
- ##
-
- package IO::Socket::INET;
-
- use strict;
- use vars qw(@ISA);
- use Socket;
- use Carp;
- use Exporter;
-
- @ISA = qw(IO::Socket);
-
- IO::Socket::INET->register_domain( AF_INET );
-
- my %socket_type = ( tcp => SOCK_STREAM,
- udp => SOCK_DGRAM,
- icmp => SOCK_RAW,
- );
-
- =head2 IO::Socket::INET
-
- C<IO::Socket::INET> provides a constructor to create an AF_INET domain socket
- and some related methods. The constructor can take the following options
-
- PeerAddr Remote host address <hostname>[:<port>]
- PeerPort Remote port or service <service>[(<no>)] | <no>
- LocalAddr Local host bind address hostname[:port]
- LocalPort Local host bind port <service>[(<no>)] | <no>
- Proto Protocol name (or number) "tcp" | "udp" | ...
- Type Socket type SOCK_STREAM | SOCK_DGRAM | ...
- Listen Queue size for listen
- Reuse Set SO_REUSEADDR before binding
- Timeout Timeout value for various operations
-
-
- If C<Listen> is defined then a listen socket is created, else if the
- socket type, which is derived from the protocol, is SOCK_STREAM then
- connect() is called.
-
- The C<PeerAddr> can be a hostname or the IP-address on the
- "xx.xx.xx.xx" form. The C<PeerPort> can be a number or a symbolic
- service name. The service name might be followed by a number in
- parenthesis which is used if the service is not known by the system.
- The C<PeerPort> specification can also be embedded in the C<PeerAddr>
- by preceding it with a ":".
-
- If C<Proto> is not given and you specify a symbolic C<PeerPort> port,
- then the constructor will try to derive C<Proto> from the service
- name. As a last resort C<Proto> "tcp" is assumed. The C<Type>
- parameter will be deduced from C<Proto> if not specified.
-
- If the constructor is only passed a single argument, it is assumed to
- be a C<PeerAddr> specification.
-
- Examples:
-
- $sock = IO::Socket::INET->new(PeerAddr => 'www.perl.org',
- PeerPort => 'http(80)',
- Proto => 'tcp');
-
- $sock = IO::Socket::INET->new(PeerAddr => 'localhost:smtp(25)');
-
- $sock = IO::Socket::INET->new(Listen => 5,
- LocalAddr => 'localhost',
- LocalPort => 9000,
- Proto => 'tcp');
-
- $sock = IO::Socket::INET->new('127.0.0.1:25');
-
-
- =head2 METHODS
-
- =over 4
-
- =item sockaddr ()
-
- Return the address part of the sockaddr structure for the socket
-
- =item sockport ()
-
- Return the port number that the socket is using on the local host
-
- =item sockhost ()
-
- Return the address part of the sockaddr structure for the socket in a
- text form xx.xx.xx.xx
-
- =item peeraddr ()
-
- Return the address part of the sockaddr structure for the socket on
- the peer host
-
- =item peerport ()
-
- Return the port number for the socket on the peer host.
-
- =item peerhost ()
-
- Return the address part of the sockaddr structure for the socket on the
- peer host in a text form xx.xx.xx.xx
-
- =back
-
- =cut
-
- sub new
- {
- my $class = shift;
- unshift(@_, "PeerAddr") if @_ == 1;
- return $class->SUPER::new(@_);
- }
-
- sub _sock_info {
- my($addr,$port,$proto) = @_;
- my @proto = ();
- my @serv = ();
-
- $port = $1
- if(defined $addr && $addr =~ s,:([\w\(\)/]+)$,,);
-
- if(defined $proto) {
- @proto = $proto =~ m,\D, ? getprotobyname($proto)
- : getprotobynumber($proto);
-
- $proto = $proto[2] || undef;
- }
-
- if(defined $port) {
- $port =~ s,\((\d+)\)$,,;
-
- my $defport = $1 || undef;
- my $pnum = ($port =~ m,^(\d+)$,)[0];
-
- @serv= getservbyname($port, $proto[0] || "")
- if($port =~ m,\D,);
-
- $port = $pnum || $serv[2] || $defport || undef;
-
- $proto = (getprotobyname($serv[3]))[2] || undef
- if @serv && !$proto;
- }
-
- return ($addr || undef,
- $port || undef,
- $proto || undef
- );
- }
-
- sub _error {
- my $fh = shift;
- $@ = join("",ref($fh),": ",@_);
- carp $@ if $^W;
- close($fh)
- if(defined fileno($fh));
- return undef;
- }
-
- sub configure {
- my($fh,$arg) = @_;
- my($lport,$rport,$laddr,$raddr,$proto,$type);
-
-
- ($laddr,$lport,$proto) = _sock_info($arg->{LocalAddr},
- $arg->{LocalPort},
- $arg->{Proto});
-
- $laddr = defined $laddr ? inet_aton($laddr)
- : INADDR_ANY;
-
- return _error($fh,"Bad hostname '",$arg->{LocalAddr},"'")
- unless(defined $laddr);
-
- unless(exists $arg->{Listen}) {
- ($raddr,$rport,$proto) = _sock_info($arg->{PeerAddr},
- $arg->{PeerPort},
- $proto);
- }
-
- if(defined $raddr) {
- $raddr = inet_aton($raddr);
- return _error($fh,"Bad hostname '",$arg->{PeerAddr},"'")
- unless(defined $raddr);
- }
-
- $proto ||= (getprotobyname "tcp")[2];
- return _error($fh,'Cannot determine protocol')
- unless($proto);
-
- my $pname = (getprotobynumber($proto))[0];
- $type = $arg->{Type} || $socket_type{$pname};
-
- $fh->socket(AF_INET, $type, $proto) or
- return _error($fh,"$!");
-
- if ($arg->{Reuse}) {
- $fh->sockopt(SO_REUSEADDR,1) or
- return _error($fh);
- }
-
- $fh->bind($lport || 0, $laddr) or
- return _error($fh,"$!");
-
- if(exists $arg->{Listen}) {
- $fh->listen($arg->{Listen} || 5) or
- return _error($fh,"$!");
- }
- else {
- return _error($fh,'Cannot determine remote port')
- unless($rport || $type == SOCK_DGRAM || $type == SOCK_RAW);
-
- if($type == SOCK_STREAM || defined $raddr) {
- return _error($fh,'Bad peer address')
- unless(defined $raddr);
-
- $fh->connect($rport,$raddr) or
- return _error($fh,"$!");
- }
- }
-
- $fh;
- }
-
- sub sockaddr {
- @_ == 1 or croak 'usage: $fh->sockaddr()';
- my($fh) = @_;
- (sockaddr_in($fh->sockname))[1];
- }
-
- sub sockport {
- @_ == 1 or croak 'usage: $fh->sockport()';
- my($fh) = @_;
- (sockaddr_in($fh->sockname))[0];
- }
-
- sub sockhost {
- @_ == 1 or croak 'usage: $fh->sockhost()';
- my($fh) = @_;
- inet_ntoa($fh->sockaddr);
- }
-
- sub peeraddr {
- @_ == 1 or croak 'usage: $fh->peeraddr()';
- my($fh) = @_;
- (sockaddr_in($fh->peername))[1];
- }
-
- sub peerport {
- @_ == 1 or croak 'usage: $fh->peerport()';
- my($fh) = @_;
- (sockaddr_in($fh->peername))[0];
- }
-
- sub peerhost {
- @_ == 1 or croak 'usage: $fh->peerhost()';
- my($fh) = @_;
- inet_ntoa($fh->peeraddr);
- }
-
- ##
- ## AF_UNIX
- ##
-
- package IO::Socket::UNIX;
-
- use strict;
- use vars qw(@ISA $VERSION);
- use Socket;
- use Carp;
- use Exporter;
-
- @ISA = qw(IO::Socket);
-
- IO::Socket::UNIX->register_domain( AF_UNIX );
-
- =head2 IO::Socket::UNIX
-
- C<IO::Socket::UNIX> provides a constructor to create an AF_UNIX domain socket
- and some related methods. The constructor can take the following options
-
- Type Type of socket (eg SOCK_STREAM or SOCK_DGRAM)
- Local Path to local fifo
- Peer Path to peer fifo
- Listen Create a listen socket
-
- =head2 METHODS
-
- =over 4
-
- =item hostpath()
-
- Returns the pathname to the fifo at the local end
-
- =item peerpath()
-
- Returns the pathanme to the fifo at the peer end
-
- =back
-
- =cut
-
- sub configure {
- my($fh,$arg) = @_;
- my($bport,$cport);
-
- my $type = $arg->{Type} || SOCK_STREAM;
-
- $fh->socket(AF_UNIX, $type, 0) or
- return undef;
-
- if(exists $arg->{Local}) {
- my $addr = sockaddr_un($arg->{Local});
- $fh->bind($addr) or
- return undef;
- }
- if(exists $arg->{Listen}) {
- $fh->listen($arg->{Listen} || 5) or
- return undef;
- }
- elsif(exists $arg->{Peer}) {
- my $addr = sockaddr_un($arg->{Peer});
- $fh->connect($addr) or
- return undef;
- }
-
- $fh;
- }
-
- sub hostpath {
- @_ == 1 or croak 'usage: $fh->hostpath()';
- my $n = $_[0]->sockname || return undef;
- (sockaddr_un($n))[0];
- }
-
- sub peerpath {
- @_ == 1 or croak 'usage: $fh->peerpath()';
- my $n = $_[0]->peername || return undef;
- (sockaddr_un($n))[0];
- }
-
- =head1 SEE ALSO
-
- L<Socket>, L<IO::Handle>
-
- =head1 AUTHOR
-
- Graham Barr E<lt>F<Graham.Barr@tiuk.ti.com>E<gt>
-
- =head1 COPYRIGHT
-
- Copyright (c) 1996 Graham Barr. All rights reserved. This program is free
- software; you can redistribute it and/or modify it under the same terms
- as Perl itself.
-
- =cut
-
- 1; # Keep require happy
-